// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ImageFrameTest.cs" company="Microsoft Corporation">
// The MIT License (MIT)
// 
// Copyright (c) 2014, Microsoft Corporation
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------

namespace Test.Robotics.Vision
{
    ////using System;
    ////using System.Collections.Generic;
    ////using System.Linq;
    ////using System.Text;
    ////using System.Threading.Tasks;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Threading;
    using Microsoft.Robotics.Numerics;
    using Microsoft.Robotics.Runtime;
    using Microsoft.Robotics.Vision;
    using Microsoft.Robotics.Vision.Runtime.Cameras;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Test.Robotics.Runtime;

    /// <summary>
    /// Test for ImageFrame class interop
    /// </summary>
    [TestClass]
    public class ImageFrameTest
    {
        /// <summary>
        /// Validation of ImageFrame construction
        /// </summary>
        [Priority(1)]
        [TestMethod]
        [TestCategory("Unit")]
        public void ImageFrameConstructorShouldInitializeProperly()
        {
            int width = 640;
            int height = 480;

            ImageFrameGray8 imageGray8 = new ImageFrameGray8(width, height);
            ImageFrameRgb imageRgb = new ImageFrameRgb(width, height);
            ImageFrameYuy2 imageYuy2 = new ImageFrameYuy2(width, height);
            ImageFrameDepth imageDepth = new ImageFrameDepth(width, height);

            Assert.AreEqual(width, imageGray8.Width);
            Assert.AreEqual(width, imageRgb.Width);
            Assert.AreEqual(width, imageYuy2.Width);
            Assert.AreEqual(width, imageDepth.Width);

            Assert.AreEqual(height, imageGray8.Height);
            Assert.AreEqual(height, imageRgb.Height);
            Assert.AreEqual(height, imageYuy2.Height);
            Assert.AreEqual(height, imageDepth.Height);

            Assert.AreEqual(1, imageGray8.BytesPerPixel);
            Assert.AreEqual(3, imageRgb.BytesPerPixel);
            Assert.AreEqual(2, imageYuy2.BytesPerPixel);
            Assert.AreEqual(2, imageDepth.BytesPerPixel);

            Assert.AreEqual(imageGray8.Width * imageGray8.BytesPerPixel, imageGray8.Stride);
            Assert.AreEqual(imageRgb.Width * imageRgb.BytesPerPixel, imageRgb.Stride);
            Assert.AreEqual(imageYuy2.Width * imageYuy2.BytesPerPixel, imageYuy2.Stride);
            Assert.AreEqual(imageDepth.Width * imageDepth.BytesPerPixel, imageDepth.Stride);

            int x, y;
            for (y = 0; y < height; y++)
            {
                for (x = 0; x < width; x++)
                {
                    Assert.AreEqual(imageGray8[x, y], 0);
                    Assert.AreEqual(imageRgb[x, y], new RgbPix(0, 0, 0));
                    Assert.AreEqual(imageYuy2[x, y], new Yuy2Pix(0, 0));
                    Assert.AreEqual(imageDepth[x, y], 0);
                }
            }
        }

        /// <summary>
        /// Validation of ImageFrame pixel access
        /// </summary>
        [Priority(1)]
        [TestMethod]
        [TestCategory("Unit")]
        public void ImageFrameShouldAccessPixelsProperly()
        {
            int width = 640;
            int height = 480;

            ImageFrameGray8 imageGray8 = new ImageFrameGray8(width, height);
            ImageFrameRgb imageRgb = new ImageFrameRgb(width, height);
            ImageFrameYuy2 imageYuy2 = new ImageFrameYuy2(width, height);
            ImageFrameDepth imageDepth = new ImageFrameDepth(width, height);

            Random rand = new Random();
            int x = rand.Next(0, width);
            int y = rand.Next(0, height);
            byte valGray8 = (byte)rand.Next(0, 256);
            RgbPix valRgb = new RgbPix((byte)rand.Next(0, 256), (byte)rand.Next(0, 256), (byte)rand.Next(0, 256));
            Yuy2Pix valYuy2 = new Yuy2Pix((byte)rand.Next(0, 256), (byte)rand.Next(0, 256));
            short valDepth = (short)rand.Next(0, 256 * 256);

            imageGray8[x, y] = valGray8;
            Assert.AreEqual(imageGray8[x, y], valGray8);

            imageRgb[x, y] = valRgb;
            Assert.AreEqual(imageRgb[x, y], valRgb);

            imageYuy2[x, y] = valYuy2;
            Assert.AreEqual(imageYuy2[x, y], valYuy2);

            imageDepth[x, y] = valDepth;
            Assert.AreEqual(imageDepth[x, y], valDepth);
        }

        /// <summary>
        /// Validation of ImageFrame cloning
        /// </summary>
        [Priority(1)]
        [TestMethod]
        [TestCategory("Unit")]
        public void ImageFrameClone()
        {
            Random rand = new Random(25);

            ImageFrame<short> orig = new ImageFrame<short>(10, 20);
            this.InitializeImageFrameShort(rand, orig);

            ImageFrame<short> shallow = new ImageFrame<short>(orig);
            this.CheckIfEqual(orig, shallow);

            ImageFrame<short> clone = (ImageFrame<short>)orig.Clone();
            this.CheckIfEqual(orig, clone);

            int x = 5;
            int y = 7;
            short unalter = orig[x, y];
            orig[x, y] = (short)(short.MaxValue - orig[x, y]);

            // Shallow copy is also changed
            Assert.AreEqual(orig[x, y], shallow[x, y]);

            // Deep copy retains the unaltered value
            Assert.AreEqual(unalter, clone[x, y]);
        }

        /// <summary>
        /// Validation of derived ImageFrame conversions
        /// </summary>
        [Priority(1)]
        [TestMethod]
        [TestCategory("Unit")]
        public void ImageFrameConversion()
        {
            ImageFrame<RgbPix> origRGB = new ImageFrame<RgbPix>(10, 20);
            ImageFrameRgb convertRGB = origRGB;
            Assert.AreEqual(convertRGB.Width, origRGB.Width);
            Assert.AreEqual(convertRGB.Height, origRGB.Height);

            ImageFrame<Yuy2Pix> origYu2 = new ImageFrame<Yuy2Pix>(20, 40);
            ImageFrameYuy2 convertYu2 = origYu2;
            Assert.AreEqual(convertYu2.Width, origYu2.Width);
            Assert.AreEqual(convertYu2.Height, origYu2.Height);
        }

        /// <summary>
        /// Validation of RGBPix operations
        /// </summary>
        [Priority(1)]
        [TestMethod]
        [TestCategory("Unit")]
        public void RGBpixOperations()
        {
            RgbPix orig = new RgbPix(2, 4, 6);
            RgbPix mult2 = orig * 2.0;
            RgbPix div2 = orig / 2.0;
            RgbPix plus = orig + mult2;

            Assert.AreEqual(mult2.Red, orig.Red * 2);
            Assert.AreEqual(mult2.Green, orig.Green * 2);
            Assert.AreEqual(mult2.Blue, orig.Blue * 2);

            Assert.AreEqual(div2.Red, orig.Red / 2);
            Assert.AreEqual(div2.Green, orig.Green / 2);
            Assert.AreEqual(div2.Blue, orig.Blue / 2);

            Assert.AreEqual(plus.Red, orig.Red + mult2.Red);
            Assert.AreEqual(plus.Green, orig.Green + mult2.Green);
            Assert.AreEqual(plus.Blue, orig.Blue + mult2.Blue);

            RgbPix origOther = mult2 / 2;
            Assert.AreEqual(orig, origOther);
        }

        /// <summary>
        /// Test the RGB method to byte array
        /// </summary>
        [Priority(1)]
        [TestMethod]
        [TestCategory("Unit")]
        public void RGBImageToByte()
        {
            int width = 3;
            int height = 5;

            ImageFrameRgb rgb = new ImageFrameRgb(width, height);
            for (int i = 0; i < width * height; ++i)
            {
                byte val = (byte)(i % byte.MaxValue);
                RgbPix pix = new RgbPix(val, val, val);
                rgb[i] = pix;
            }

            byte[] rgbAsByte = rgb.GetAsByteArray();
                
            int k = 0;
            for (int i = 0; i < width * height; ++i)
            {
                byte val = (byte)(i % byte.MaxValue);
                for (int j = 0; j < rgb.BytesPerPixel; ++j)
                {
                    Assert.AreEqual(rgbAsByte[k++], val);
                }
            }
        }

        /// <summary>
        /// Image frame depth construction test
        /// </summary>
        [Priority(1)]
        [TestMethod]
        [TestCategory("Unit")]
        public void ImageFrameDepthConstruction()
        {
            int size = 5;
            ImageFrame<short> orig = new ImageFrame<short>(size, size);
            Random rand = new Random(35);
            this.InitializeImageFrameShort(rand, orig);

            ImageFrameDepth depth1 = new ImageFrameDepth(orig);
            for (int i = 0; i < size * size; ++i)
            {
                Assert.AreEqual(depth1[i], orig[i]);
            }

            ushort[] short1 = depth1.GetDataAsUnsigned();

            for (int i = 0; i < size * size; ++i)
            {
                Assert.AreEqual(short1[i], (ushort)orig[i]);
            }
        }

        /// <summary>
        /// Randomly initialize an image
        /// </summary>
        /// <param name="rand">Random generator</param>
        /// <param name="imageFrame">Image to initialize</param>
        private void InitializeImageFrameShort(Random rand, ImageFrame<short> imageFrame)
        {
            for (int i = 0; i < imageFrame.Width; ++i)
            {
                for (int j = 0; j < imageFrame.Height; ++j)
                {
                    imageFrame[i, j] = (short)rand.Next(0, short.MaxValue);
                }
            }
        }

        /// <summary>
        /// Checks if two images are equal
        /// </summary>
        /// <param name="im1">First image</param>
        /// <param name="im2">Second Image</param>
        private void CheckIfEqual(ImageFrame<short> im1, ImageFrame<short> im2)
        {
            Assert.AreEqual(im1.Width, im2.Width);
            Assert.AreEqual(im1.Height, im2.Height);

            for (int i = 0; i < im1.Width; ++i)
            {
                for (int j = 0; j < im1.Height; ++j)
                {
                    Assert.AreEqual(im1[i, j], im2[i, j]);
                }
            }
        }
    }
}
